package gbench.sandbox.zk.zoo;

import static gbench.common.tree.LittleTree.Term.FT;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.common.fs.FileSystem;
import gbench.common.matlib.data.DataReader.DFrame;
import gbench.common.matlib.rdd.BasePair;
import gbench.common.tree.LittleTree.Term;

/**
 * 
 * @author gbench
 *
 */
public class ZKYmlEngine {
    
    /**
     * yml 的格式解析
     * 
     * @param <U>      属性值类型
     * @param bpStream 数据流 [(String,u)]
     * @param action     回调函数 (node,u) -> {}
     * @return TrieNode 根节点的值 为 rootName
     */
    public <U> ZKTrieNode<String> yml(final Stream<BasePair<String, U>> bpStream,
            final BiConsumer<ZKTrieNode<String>, U> action) {

        final BiFunction<String, String, String> merge = (a, b) -> Term.FT("$0,$1", a, b.strip());
        final Function<AtomicReference<String>, Function<String, String>> continue_line = a_pre_lines -> e -> {
            final var line = e.replaceAll("[\\\\]*$", ""); // 去除尾部的 续行 符号 \
            if (e.endsWith("\\")) { // 包含有 下一行
                a_pre_lines.updateAndGet(s -> { // 设置当前行
                    return s == null ? line : merge.apply(s, line);
                });
                return null; // 返回空值 用以表名 这是一个未完成的行，需要后面的 处理函数 给与过滤掉 这一null行。
            } else { // 没有续行符
                final var pre_lines = a_pre_lines.get(); // 获取前一行数据
                if (pre_lines == null) { // 没有前导行
                    return line;
                } else { // 包含前导行
                    a_pre_lines.set(null); // 清空前一行数据
                    return merge.apply(pre_lines, line);
                } // if
            } // if
        }; // 续行函数 continue_line
        final var indent_lines = bpStream
                .map(e -> BasePair.bp(continue_line.apply(new AtomicReference<String>()).apply(e._1()), e._2())) // 续行计算
                .filter(bp -> bp._1() != null && !bp._1().matches("\\s*")) // 去除空行 和 null 值
                .collect(Collectors.toList());
        final var n = indent_lines.size(); // 行数目
        final var indent_pattern = Pattern.compile("^\\s*"); // 缩进的pattern
        final var indent_entries = new TreeMap<Integer, String>(); // TreeMap 键值排序，层级结构
        final var trie = new ZKTrieNode<String>(rootName, LinkedHashMap::new); // Trie 根结构

        for (int i = 0; i < n; i++) { // 逐行处理
            final var indent_line = indent_lines.get(i);
            final var line = indent_line._1();
            final var matcher = indent_pattern.matcher(line); // 缩进侦测
            if (matcher.find()) { // 满足缩进格式
                final var indent = matcher.group().replaceAll("\t", " ".repeat(this.indent_unit)); // 获取缩进距离，替换 \t 为 空格
                final int n_indent = indent.length() % this.indent_unit == 0 ? indent.length() // 缩进长度
                        : this.indent_unit * ((int) (indent.length() / this.indent_unit + 1)); // 向上取整
                final var level = n_indent / this.indent_unit; // 当前数据项目的的层级深度, 每个tab 键名 采用 this.indent_unit 个 空格进行划分
                indent_entries.put(level, line.strip()); // 记录键值的层级

                if (n_indent % this.indent_unit != 0) { // 提取非格式化部分
                    System.err.println(FT("error: no: $0\t level:$1\t remainder:$2 \t line:$3", i + 1, level,
                            n_indent % this.indent_unit, line));
                } else {
                    final var flds = new LinkedList<String>(); // 字段列表
                    indent_entries.entrySet() // 获取行项目
                            .stream() // 层级的排序 按照 层级深度进行排序, 层级越深 表示 数据越细节
                            .filter(e -> e.getKey() <= level) // 提取 当前层级 以上的所有 摘要层级
                            .forEach(e -> { // 层级深度
                                flds.add(e.getValue());
                            }); // 构造字段集合
                    final var node = trie.addParts(flds).flag(false);
                    action.accept(node, indent_line._2()); // 回调函数
                } // if
            } else {
                System.err.println(line);
            } // if
        } // for

        return trie;
    }

    /**
     * yml 的格式解析
     * 
     * @param file        文件路径
     * @param start_level 开始层级，从0开始
     * @return TrieNode 根节点的值 为 rootName
     */
    public ZKTrieNode<String> yml(final String file) {
        final var path = FileSystem.fileOf(file, this.getClass()); // 文件路径
        return yml(FileSystem.utf8lines(path).map(e -> BasePair.bp(e, e)), (n, u) -> {
        });
    }

    /**
     * yml 的格式解析
     * 
     * @param dfm        文件路径
     * @return TrieNode 根节点的值 为  rootName
     */
    public ZKTrieNode<String> yml(final DFrame dfm) {
        final var lineS = dfm.rowsS(e -> BasePair.bp(e.str(0), e)) // 提取第一列作为结构目录行
                .filter(p -> !p._1().equals("null")); // 去除掉空值行

        return yml(lineS, (node, u) -> {
            node.attrs("root", u);
        });
    }
    
    /**
     * 设置根节点名称
     * @param rootName 根节点名称
     * @return YmlEngine 对象本身，便于实现连式编程。
     */
    public ZKYmlEngine setRootName(String rootName) {
        this.rootName = rootName;
        return this;
    }

    private String rootName = null;
    public int indent_unit = 4;
}